﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//自定义类型
//1.结构体 -- 关键字是struct
// 每个成员都有自己独立的空间
//2.联合体/共用体 -- 关键字是union
// 联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫：共⽤体。
//3.枚举

////定义一个联合体的类型
//union Un //联合体的声明
//{
//	char c;
//	int i;
//};
//int main()
//{
//	union Un u = {0};//用union Un这个联合体类型创建一个变量u 并初始化为0
//	printf("%zd\n", sizeof(u));	//4
//	
//	
//	printf("%p\n", &u);
//	printf("%p\n", &(u.c));
//	printf("%p\n", &(u.i));
//	//这三个地址都是相同的
//	//c和i共用的是同一块空间 在改变其中的一个的话另一个也会受到影响
//	//所以在使用联合体的时候，使用一个成员变量即可 不然会起冲突
//	return 0;
//}

//union Un
//{
//	char a;
//	int i;
//};
//int main()
//{
//	union Un u = { 0 };
//	u.i = 0x11223344;
//	u.a = 0x55;//在这的话a和i的地址相同 也说明了i存放的是低地址处的地址
//	//在进行调试过程中 首先开辟了4字节的空间 因为vs是小端存储 所以vs会倒着存储 44 33 22 11 
//	//在调试完a的时候 这个44会变成55 因为他们共用一块空间 所以会影响i的值 
//	//联合的成员是共⽤同⼀块内存空间的，这样⼀个联合变量的⼤⼩，⾄少是最⼤成员的⼤⼩（因为联合⾄少得有能⼒保存最⼤的那个成员）。
//	//改变a的时候 其实相当于i的值也被改了 改的是低地址处的那个值
//	return 0;
//}

//union Un
//{
//	char c[5];//5
//	//这的话，应该是按5个char来算的 所以对齐数就是1
//	int i;//4
//	//而这个的对齐数自身是4 默认对齐数是8 所以对齐数是4
//	// 而所有成员最大对齐数是4
//	//联合体的大小也得是最大对齐数的整数倍 5并不是4的整数倍 所以会浪费3个字节，结果是8
//};
////联合的⼤⼩⾄少是最⼤成员的⼤⼩。
////当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候，就要对⻬到最⼤对⻬数的整数倍。
//int main()
//{
//	union Un u = { 0 };
//	printf("%zd\n", sizeof(u));//结果并不是最大的成员的大小5 而是8
//	//因此不可以简单说联合体的大小就是最大成员的大小
//
//	return 0;
//}

//union Un
//{
//	short s[7];//14 自身对齐数为2 默认对齐数为8 对齐数为2
//	int i;//4 自身对齐数为4 默认对齐数为8 对齐数为4
//	//最大对齐数是4 而14不是4的整数倍数 则就浪费两个空间 大小是16
//};
//int main()
//{
//	union Un u = { 0 };
//	printf("%zd\n", sizeof(u));//16
//	return 0;
//}

//struct gift_list
//{
//	//公共属性
//	int stock_number;//库存量
//	double price; //定价
//	int item_type;//商品类型
//
//	//这个联合体在这也匿名了 在定义的时候使用一次
//	//不会把所有属性都用上 选择性的开辟了空间 不会造成空间的浪费
//	union {
//		struct
//		{
//			char title[20];//书名
//			char author[20];//作者
//			int num_pages;//⻚数
//		}book;//这个也可以不匿名的 因为这种属性一般使用一次 所以选择在定义的时候用一次 就选用了匿名
//		struct
//		{
//			char design[30];//设计
//		}mug;
//		struct
//		{
//			char design[30];//设计
//			int colors;//颜⾊
//			int sizes;//尺⼨
//		}shirt;
//	}item;//特殊属性
//};
//int main()
//{
//	struct gift_list gl;
//	//...可以赋值
//	return 0;
//}

////写⼀个程序，判断当前机器是⼤端？还是⼩端？
//int check_sys()
//{
//	int n = 1;
//	return *(char*)&n;//(char*)&n 先取出地址 再强转为char* 访问的是一个字节的地址 再解引用的话访问的就是一个字节的数据
//}
//int main()
//{
//	int ret = check_sys();//根据返回值来判断
//	//返回1 是小端
//	//返回0 是大端
//	if (ret == 1)
//	{
//		printf("小端\n");
//	}
//	else
//		printf("大端\n");
//	return 0;
//}

////使用联合体来完成这个任务 写⼀个程序，判断当前机器是⼤端？还是⼩端？
////该程序就属于联合体的巧妙应用
//int check_sys()
//{
//	union Un
//	{
//		char c;
//		int i;
//	}u;
//	u.i = 1;
//	return u.c;
//	//向内存申请开辟4字节空间 刚开始初始化为0，1的二进制为1 按小端存储的话这个1肯定会放在低地址处 也就是会放在c这一个字节的位置
//	//（放在i的低地址处） 如果c的值由0变为1了 那么当前这个机器采用的就是小端存储 如果值没有变 那么采用的就是大端存储
//}
//int main()
//{
//	int ret = check_sys();
//	if (ret == 1)
//	{
//		printf("小端\n");
//	}
//	else
//		printf("大端\n");
//	return 0;
//}

//枚举类型
//枚举顾名思义就是一一列举 把可能取值一一列举出来
//枚举的关键字enum
//enum Color //颜色的枚举类型
//{
//	//三原色的可能取值 在默认不给值的时候就是从前往后 0 1 2 依次往后递增1
//	RED,//0  //这个RED赋值为5的话- RED = 5， GREEN 和 BULE分别就是6和7
//	GREEN,//1 //如果从中间开始赋值的话 依次向后递增 但第一个就是从0开始的 
//	BULE//2    这三个本身也是有值的 但是他们是枚举常量 代表的是这个枚举类型创建的变量的可能取值
//};
//
//enum Sex //性别的枚举类型
//{
//	MALE,
//	FEMALE,
//	SECRET
//};
////枚举类型是可以用来创建变量的
//int main()
//{
//	//enum Color color = RED;
//	
//	printf("%d\n", RED);//0
//	printf("%d\n", GREEN);//1
//	printf("%d\n", BULE);//2
//	
//	//RED = 5;//这是错误的 不能改这个值
//	return 0;
//}

////下边这两个进行对比
//enum Sex
//{
//	MALE,//0
//	FEMALE,//1
//	SECRET//2
//};
////而#define定义的标识符就没有类型
//#define MALE 0  //这个#define的作用就是把整个工程中出现的MALE都当作0
//#define FEMALE 1
//#define SECRET 2
////这时候就要体现出枚举的优点
////1. 增加代码的可读性和可维护性
////2. 和#define定义的标识符⽐较枚举有类型检查，更加严谨。
////3. 便于调试，预处理阶段会删除 #define 定义的符号 
//// 例如 #define Male 0  在主函数中 int sex = Male; 编译器在执行之前已经把Male替换为0了 已经做了预处理命令 不好进行调试了 就是说看到的代码与实际的已经不一样了
////4. 使⽤⽅便，⼀次可以定义多个常量
////5. 枚举常量是遵循作⽤域规则的，枚举声明在函数内，只能在函数内使⽤
//void menu()//计算器功能
//{
//	printf("***************************\n");
//	printf("********1.add  2.sub ******\n");
//	printf("********3.mul  4.div ******\n");
//	printf("********* 0 . exit  *******\n");
//	printf("***************************\n");
//}
//enum Option//枚举中的这些常量是有类型的 而#define定义的标识符就没有类型
//{
//	EXIT,//0
//	ADD,//1
//	SUB,//2
//	MUL,//3
//	DIV//4 类似于给一些常量值起了名字	
//};
//int main()
//{
//	int input = 0;
//	printf("请选择:>");
//	scanf("%d", &input);
//	switch (input)
//	{
//	case ADD://这种符号就有某种意义 可以直接让人联想到这个是完成什么功能  可读性和可维护性增加了很多
//		break;
//	case SUB:
//		break;
//	case MUL:
//		break;
//	case DIV:
//		break;
//	case EXIT:
//		break;
//	default:
//		break;
//	}
//	return 0;
//}

//动态内存管理
//不管是c还是c++都会大量使用
//使用c或者c++实现数据结构的时候也会使用动态内存管理

//补充 普通数组的元素地址是从低到高增长的。栈的生长方向（函数调用、局部变量）可能是从高到低，但数组内部的元素仍是低地址到高地址

//int n = 10;
//char c = 'w';
//char arr[20];//20个字节
//int arr1[50];//200个字节
//上边这几个内存的申请方式，一旦申请好空间，大小就无法调整
//体现了内存开辟了之后显得不灵活 
//让程序员自己来动态的申请空间 C/C++中就可以 但是如果内存管理不当 就会造成负面影响